#include "game_client_impl.h"
#include "client_connection.h"
#include "service.h"
#include "game_base_msg.h"
#include "jmsg.h"
#include "game_client.h"
#include "game_client_player.h"
#include "game_client_room_status.h"
GameClientImpl::GameClientImpl( const std::string& host, int port )
{ 
	m_roomId = -1;
	m_deskId = -1;
	m_chairId = -1;
	m_player = nullptr;
	m_roomStatus = nullptr;
	m_proto = game_base_msgCreateProto();
	m_service = new Service;
	memset(m_deskPlayer, 0, sizeof(m_deskPlayer));
	m_clientConn = new ClientConnection;
	m_clientConn->init(m_service, host, port);
	m_clientConn->setHandler(this);
	m_service->start();
}

GameClientImpl::~GameClientImpl(void) {
	if (m_clientConn) {
		m_clientConn->close();
	}

	if (m_proto) {
		delete m_proto;
	}

	if (m_player) {

		if (m_player->m_chairId >= 0) {
			m_deskPlayer[m_player->m_chairId] = nullptr;
		}

		delete m_player;
	}

	if (m_roomStatus) {
		delete m_roomStatus;
	}

	for (int i = 0; i < 4; i++) {
		if (m_deskPlayer[i]) {
			delete m_deskPlayer[i];
			m_deskPlayer[i] = nullptr;
		}
	}
}

void GameClientImpl::login( const std::string& userName, const std::string& password, bool isGuest )
{
	BaseMsgC2SLogin msg;
	msg.userName = userName;
	msg.password = password;
	msg.isGuest = isGuest;

	JMsgWriter writer;
	msg.encode(m_proto, &writer);
	sendClientData(1, 0, writer.getBuffer(), writer.getBufferLen());
}

void GameClientImpl::signUp(const std::string & userName, const std::string & password)
{
	BaseMsgC2SRegister msg;
	msg.userName = userName;
	msg.passwordSha1 = password;
	JMsgWriter writer;
	msg.encode(m_proto, &writer);
	sendClientData(1, 0, writer.getBuffer(), writer.getBufferLen());
}

void GameClientImpl::onClientConnected( ClientConnection* conn )
{
	m_gameClient->onGameClientConnected();
}

bool GameClientImpl::onClientDisconnected( ClientConnection* conn )
{
	return true;
}

void GameClientImpl::onClientData( ClientConnection* conn, int mainCmd, int subCmd, void* data, int len )
{
	bool isSuccess = true;

	if (subCmd == 0) {
		string dataRecv;
		JMsgReader jsonReader((unsigned char*)data, len);
		int typeId = jsonReader.peekMessageTypeId(isSuccess);
		JMsgType* type = m_proto->getTypeById(typeId);

		if (!type) {
			return;
		}

		m_proto->toJson(&jsonReader, -1, dataRecv);
		if (m_gameClient) {
			m_gameClient->onGameClientRecvData(type->m_typeName, dataRecv);
		}
	}	

	if(mainCmd == 1) {
		if(subCmd == 0) {
			this->procRoomSystemMsg(data, len);
		} else {
			this->procRoomUserMsg(data, len);
		}
	} else {
		if(subCmd == 0) {
			this->procDeskSystemMsg(data, len);
		} else {
			this->procDeskUserMsg(data, len);
		}
	}
}

void GameClientImpl::procRoomSystemMsg( void* data, int len )
{
	JMsgReader reader((unsigned char*)data, len);
	bool success = true;
	int msgId = reader.peekMessageTypeId(success);

	if(msgId == kBaseMsgS2CLogin) {
		BaseMsgS2CLogin msg;
		msg.decode(m_proto, &reader);
		this->procLoginResult(&msg);
	}
	else if (msgId == kBaseMsgS2CRegister) {
		BaseMsgS2CRegister msg;
		msg.decode(m_proto, &reader);
		this->procRegisterResult(&msg);
	}
	else if (msgId == kBaseMsgS2CActivate) {
		BaseMsgS2CActivate msg;
		msg.decode(m_proto, &reader);
		this->procActivateResult(&msg);
	}
	else if (msgId == kBaseMsgS2CJoinRoom) {
		BaseMsgS2CJoinRoom msg;
		msg.decode(m_proto, &reader);
		this->procJoinRoomResult(&msg);
	}
}

void GameClientImpl::sendClientData(int mainCmd, int subCmd, void* data, int len) {
	JMsgReader jsonReader((unsigned char*)data, len);
	bool isSuccess;
	int typeId = jsonReader.peekMessageTypeId(isSuccess);
	JMsgType* type = m_proto->getTypeById(typeId);

	if (!type) {
		return;
	}

	string jsonStr;
	m_proto->toJson(&jsonReader, 0, jsonStr);

	if (m_gameClient) {
		m_gameClient->onGameClientSendData(type->m_typeName, jsonStr);
	}
	m_clientConn->sendDBData(mainCmd, subCmd, data, len);
}

void GameClientImpl::procRoomUserMsg( void* data, int len )
{
	if (m_gameClient) {
		m_gameClient->onGameClientRoomMessage(data, len);
	}
}

void GameClientImpl::procDeskUserMsg( void* data, int len )
{
	if (m_gameClient) {
		m_gameClient->onGameClientDeskMessage(data, len);
	}
}

void GameClientImpl::procDeskSystemMsg( void* data, int len )
{
	JMsgReader reader((unsigned char*)data, len);
	bool success = true;
	int msgId = reader.peekMessageTypeId(success);

	if (msgId == kBaseMsgS2CJoinDesk) {
		BaseMsgS2CJoinDesk msg;
		msg.decode(m_proto, &reader);
		this->procJoinDeskResult(&msg);
	}
	else if (msgId == kBaseMsgDeskS2COtherPlayerJoinDesk) {
		BaseMsgDeskS2COtherPlayerJoinDesk msg;
		msg.decode(m_proto, &reader);
		this->procOtherPlayerJoinDesk(&msg);
	}
	else if (msgId == kBaseMsgDeskS2COtherPlayerLeaveDesk) {
		BaseMsgDeskS2COtherPlayerLeaveDesk msg;
		msg.decode(m_proto, &reader);
		this->procOtherPlayerLeaveDesk(&msg);
	}
	else if (msgId == kBaseMsgS2CCreateFriendDesk) {
		BaseMsgS2CCreateFriendDesk msg;
		msg.decode(m_proto, &reader);
		this->procCreateFriendResult(&msg);
	}
}

void GameClientImpl::procLoginResult( BaseMsgS2CLogin* msg )
{
	if (msg->success) {
		m_player = new GameClientPlayer;
		m_player->m_userName = msg->userName;
		BaseMsgPlayerInfo& playerInfo = msg->playerInfo;
		m_player->m_isGuest = playerInfo.isGuest;
		m_player->m_lastLoginTime = playerInfo.lastLoginTime;
		m_player->m_nickName = playerInfo.nickName;
		m_player->m_score = playerInfo.score;
	}

	if (m_gameClient) {
		m_gameClient->onGameClientLogin(msg->success ? true : false);
	}
}

void GameClientImpl::procRegisterResult(BaseMsgS2CRegister * msg)
{
	if (m_gameClient) {
		m_gameClient->onGameClientRegister(msg->isSuccess, msg->errorCode);
	}
}

void GameClientImpl::procActivateResult(BaseMsgS2CActivate * msg)
{
	if (!m_player) {
		return;
	}

	if (msg->isSuccess) {
		m_player->m_userName = msg->newUserName;
		m_player->m_isGuest = false;
	}

	if (m_gameClient) {
		m_gameClient->onGameClientActivate(msg->isSuccess, msg->errorCode);
	}
}

void GameClientImpl::procJoinRoomResult(BaseMsgS2CJoinRoom* msg) {
	if (!m_player) {
		return;
	}

	if (msg->isSuccess) {
		this->m_roomId = msg->roomId;
		m_roomStatus = new GameClientRoomStatus;
		m_roomStatus->playerCount = msg->roomState.currentPlayerCount;
		m_roomStatus->roomId = msg->roomId;
		m_roomStatus->roomType = msg->roomState.roomType;
	}

	if (m_gameClient) {
		m_gameClient->onGameClientJoinRoom(msg->isSuccess, msg->errorCode);
	}
}

void GameClientImpl::procJoinDeskResult(BaseMsgS2CJoinDesk* msg) {
	if (!m_player) {
		return;
	}


	if (msg->success) {
		m_deskId = msg->deskInfo.deskId;
		m_chairId = msg->chairId;
		m_deskPlayer[msg->chairId] = m_player;

		for (size_t i = 0; i < msg->deskInfo.playerNames.size(); i++) {
			BaseMsgPlayerInfo& playerInfo = msg->deskInfo.playerNames[i];
			if (playerInfo.userName == m_player->m_userName) {
				continue;
			}
			GameClientPlayer* player = createPlayerFromMsg(playerInfo);
			player->m_chairId = msg->deskInfo.playerChairIds[i];
			m_deskPlayer[msg->deskInfo.playerChairIds[i]] = player;

		}
	}	

	if (m_gameClient) {
		// todo: add error code
		m_gameClient->onGameClientJoinDesk(msg->success, 0);
	}
}

void GameClientImpl::sendRoomData( void* data, int len )
{

}

void GameClientImpl::sendDeskData( void* data, int len )
{
	m_clientConn->sendDBData(2, 1, data, len);
}

void GameClientImpl::joinDesk(int deskId, int chairId)
{
	if (!m_player) {
		return;
	}

	if (m_deskId >= 0) {
		return;
	}

	BaseMsgC2SJoinDesk msg;
	msg.deskId = deskId;
	msg.chairId = chairId;
	JMsgWriter writer;
	msg.encode(m_proto, &writer);
	sendClientData(2, 0, writer.getBuffer(), writer.getBufferLen());
}

void GameClientImpl::leaveDesk()
{
	if (!m_player) {
		return;
	}

	if (m_deskId < 0) {
		return;
	}

	m_deskId = -1;
	
	BaseMsgC2SLeaveDesk msg;
	JMsgWriter writer;
	msg.encode(m_proto, &writer);
	sendClientData(2, 0, writer.getBuffer(), writer.getBufferLen());
}

void GameClientImpl::createFriendDesk() {
	if (!m_player) {
		return;
	}

	if (m_roomId < 0) {
		return;
	}

	BaseMsgC2SCreateFriendDesk msg;
	JMsgWriter writer;
	msg.encode(m_proto, &writer);
	sendClientData(2, 0, writer.getBuffer(), writer.getBufferLen());
}

void GameClientImpl::setGameClient( GameClient* gameClient )
{
	m_gameClient = gameClient;
}

void GameClientImpl::activate(const string& newPlayerName, const string& newPassword, const std::string& phoneNo) {
	if (!m_player) {
		return;
	}

	if (!m_player->getIsGuest()) {
		return;
	}
	BaseMsgC2SActivate msg;
	msg.guestUserName = m_player->getUserName();
	msg.userName = newPlayerName;
	msg.passwordSha1 = newPassword;
	msg.phoneNo = phoneNo;
	JMsgWriter writer;
	msg.encode(m_proto, &writer);
	sendClientData(1, 0, writer.getBuffer(), writer.getBufferLen());
}

void GameClientImpl::joinRoom(int roomId) {
	if (!m_player) {
		return;
	}

	BaseMsgC2SJoinRoom msg;
	msg.roomId = roomId;
	JMsgWriter writer;
	msg.encode(m_proto, &writer);
	sendClientData(1, 0, writer.getBuffer(), writer.getBufferLen());
}

void GameClientImpl::joinFriendDesk(const std::string deskCode) {
	if (!m_player) {
		return;
	}

	BaseMsgC2SJoinFriendDesk msg;
	msg.deskCode = deskCode;
	JMsgWriter writer;
	msg.encode(m_proto, &writer);
	sendClientData(2, 0, writer.getBuffer(), writer.getBufferLen());
}

void GameClientImpl::procOtherPlayerJoinDesk(BaseMsgDeskS2COtherPlayerJoinDesk* msg) {
	if (!m_player) {
		return;
	}
	GameClientPlayer* player = new GameClientPlayer;
	player->m_userName = msg->playerInfo.userName;
	player->m_isGuest = msg->playerInfo.isGuest;
	player->m_score = msg->playerInfo.score;
	player->m_chairId = msg->chairId;
	player->m_lastLoginTime = msg->playerInfo.lastLoginTime;
	player->m_nickName = msg->playerInfo.nickName;

	m_deskPlayer[msg->chairId] = player;
	
	if (m_gameClient) {
		m_gameClient->onGameClientOtherPlayerJoinDesk(player);
	}
}

void GameClientImpl::procOtherPlayerLeaveDesk(BaseMsgDeskS2COtherPlayerLeaveDesk* msg) {
	if (!m_player) {
		return;
	}

	if (msg->userName == m_player->m_userName) {
		this->m_deskId = -1;
		this->m_chairId = -1;
		if (m_gameClient) {
			m_gameClient->onGameClientLeaveDesk(0);
		}
		memset(m_deskPlayer, 0, sizeof(m_deskPlayer));
	}
	else {
		
		if (m_gameClient) {
			m_gameClient->onGameClientOtherPlayerLeaveDesk(m_deskPlayer[msg->chairId]);
		}
		delete m_deskPlayer[msg->chairId];
		m_deskPlayer[msg->chairId] = NULL;
	}
}

void GameClientImpl::procCreateFriendResult(BaseMsgS2CCreateFriendDesk* msg) {
	if (!m_player) {
		return;
	}

	if (m_gameClient) {
		m_gameClient->onGameClientCreateFriendDesk(msg->success, msg->errorCode, msg->deskCode);
	}
}

void GameClientImpl::procRoomStatus(BaseMsgS2CUpdateRoomStatus* msg) {
	if (!m_player) {
		return;
	}

	if (!msg->success) {
		return;
	}
	if (!m_roomStatus) {
		m_roomStatus = new GameClientRoomStatus;
	}
	m_roomStatus->playerCount = msg->roomStatus.currentPlayerCount;
	m_roomStatus->roomType = msg->roomStatus.roomType;
	m_roomStatus->roomId = msg->roomStatus.roomId;

	if (m_gameClient) {
		m_gameClient->onGameClientUpdateRoomStatus();
	}
}

void GameClientImpl::leaveRoom()
{
	BaseMsgC2SLeaveRoom msg;
	JMsgWriter writer;
	msg.encode(m_proto, &writer);
	sendClientData(1, 0, writer.getBuffer(), writer.getBufferLen());	
}

void GameClientImpl::updatePlayerInfo()
{
	if (!m_player) {
		return;
	}

	if (m_player->m_newNickName == "" && m_player->m_newPhoneNo == "") {
		return;
	}

	if (m_player->m_newNickName == m_player->m_nickName && m_player->m_newPhoneNo == m_player->m_phoneNo) {
		return;
	}

	BaseMsgC2SUpdatePlayerData msg;
	msg.nickName = m_player->m_newNickName;
	msg.phoneNo = m_player->m_newPhoneNo;
	JMsgWriter writer;
	msg.encode(m_proto, &writer);
	sendClientData(1, 0, writer.getBuffer(), writer.getBufferLen());
}

void GameClientImpl::updateRoomStatus() {

	if (m_roomId < 0) {
		return;
	}

	BaseMsgC2SUpdateRoomStatus msg;
	JMsgWriter writer;
	msg.encode(m_proto, &writer);
	sendClientData(1, 0, writer.getBuffer(), writer.getBufferLen());
}

GameClientPlayer* GameClientImpl::getPlayer() {
	return m_player;
}

GameClientRoomStatus* GameClientImpl::getRoomStatus() {
	return m_roomStatus;
}

bool GameClientImpl::getDeskPlayers(std::vector<GameClientPlayer*>& players) {
	if (m_deskId < 0) {
		return false;
	}
	for (int i = 0; i < 4; i++) {
		if (m_deskPlayer[i]) {
			players.push_back(m_deskPlayer[i]);
		}
	}
	return true;
}

GameClientPlayer* GameClientImpl::createPlayerFromMsg(BaseMsgPlayerInfo& playerInfo) {
	GameClientPlayer* player = new GameClientPlayer;
	player->m_userName = playerInfo.userName;
	player->m_lastLoginTime = playerInfo.lastLoginTime;
	player->m_phoneNo = playerInfo.phoneNo;
	player->m_isGuest = playerInfo.isGuest;
	player->m_nickName = playerInfo.nickName;
	return player;
}